﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Games.Classes;

namespace Games.Forms.Games
{
	public partial class FormSnake : GameForm
	{
		
		private readonly Timer _gameLoop = new Timer();
		private readonly Timer _snakeLoop = new Timer();
		private readonly List<DrawingItem> _snake = new List<DrawingItem>();
		private DrawingItem _food;
		private int _direction; // 0 = down, 1 = left, 2 = right, 3 = up
		private float _snakeRate = 2.0f;

		public FormSnake()
		{
			InitializeComponent();
			_gameLoop.Tick += Update;
			_snakeLoop.Tick += UpdateSnake;
			StartGame();
		}

		#region Event Handlers
		private void formGame_KeyDown(object sender, KeyEventArgs e)
		{
			Input.ChangeState(e.KeyCode, true);
		}

		private void FormGame_KeyPress(object sender, KeyPressEventArgs e)
		{
			if (e.KeyChar == 'p' || e.KeyChar == 'P')
				Paused = !Paused;
		}

		private void formGame_KeyUp(object sender, KeyEventArgs e)
		{
			Input.ChangeState(e.KeyCode, false);
		}
		private void FormGame_Paint(object sender, PaintEventArgs e)
		{
			DrawBackGround(e.Graphics, pbCanvas.Width, pbCanvas.Height);
		}
		private void pbCanvas_Paint(object sender, PaintEventArgs e)
		{
			DrawSnake(e.Graphics);
		}

		private void Update(object sender, EventArgs e)
		{
			if (!GameOver)
			{
				if (Input.Press(Keys.Left) || Input.Press(Keys.A))
				{
					if (_snake.Count < 2 || _snake[0].X == _snake[1].X)
						_direction = 1;
				}
				if (Input.Press(Keys.Right) || Input.Press(Keys.D))
				{
					if (_snake.Count < 2 || _snake[0].X == _snake[1].X)
						_direction = 2;
				}
				if (Input.Press(Keys.Up) || Input.Press(Keys.W))
				{
					if (_snake.Count < 2 || _snake[0].Y == _snake[1].Y)
						_direction = 3;
				}
				if (Input.Press(Keys.Down) || Input.Press(Keys.S))
				{
					if (_snake.Count < 2 || _snake[0].Y == _snake[1].Y)
						_direction = 0;
				}
				if (Input.Press(Keys.Enter))
				{
					if (LevelUp)
					{
						LevelUp = false;
						if (Level == MAX_LEVEL)
							StartGame();
						else
							StartNextLevel();
						return;
					}
				}
			}
			else
			{
				if (Input.Press(Keys.Enter))
				{
					if(CurrentLives > 0)
						StartLevel();
					else
						StartGame();
				}
			}
			if (Input.Press(Keys.X) || Input.Press(Keys.Escape))
			{
				Close();
			}
			pbCanvas.Invalidate();
		}

		private void UpdateSnake(object sender, EventArgs e)
		{
			if (GameOver || LevelUp || Paused) return;
			for (int i = _snake.Count - 1; i >= 0; i--)
			{
				var currentPart = _snake[i];
				if (i == 0)
				{
					//it's the head
					switch (_direction)
					{
						case 0:
							_snake[i].Y++;
							break;
						case 1:
							_snake[i].X--;
							break;
						case 2:
							_snake[i].X++;
							break;
						case 3:
							_snake[i].Y--;
							break;
					}
					//check for out of bounds
					if (currentPart.X >= pbCanvas.Width / 16 || currentPart.X < 0 || currentPart.Y >= pbCanvas.Height / 16 || currentPart.Y < 0)
						Gameover();

					//check for collision with board barrier
					if (Board.Barriers.Any(a => a.X == currentPart.X && a.Y == currentPart.Y))
						Gameover();

					//check for collision with body
					for (int j = 1; j < _snake.Count; j++)
						if (currentPart.X == _snake[j].X && currentPart.Y == _snake[j].Y)
							Gameover();

					//check for collision with food
					if (currentPart.X == _food.X && currentPart.Y == _food.Y)
					{
						var snakeButt = _snake[_snake.Count - 1];
						var bodyPart = new DrawingItem(snakeButt.X, snakeButt.Y);
						_snake.Add(bodyPart);
						Score++;
						GenerateFood();
						if (Score < MAX_LEVEL_SCORE)
						{
							_snakeRate += 0.5f;
							_snakeLoop.Interval = (int) (1000/_snakeRate);
						}
						else
						{
							NextLevel();
						}
					}
				}
				else
				{
					var previousPart = _snake[i - 1];
					currentPart.X = previousPart.X;
					currentPart.Y = previousPart.Y;
				}
			}
		}
		#endregion Event Handlers

		#region Private Methods
		private void DrawSnake(Graphics canvas)
		{
			//draw the scoreboard
			var scoreString = string.Format("Level [{0}], Score [{1}], Lives [{2}]", Level, Score, CurrentLives);
			canvas.DrawString(scoreString, Font, Brushes.White, new PointF(4, 4));
			//draw the snake
			for (int i = 0; i < _snake.Count; i++)
			{
				Color snakeColor = i == 0 ? Board.SnakeHeadColor : Board.SnakeBodyColor;
				var currentPart = _snake[i];
				canvas.FillRectangle(new SolidBrush(snakeColor), new Rectangle(currentPart.X * 16, currentPart.Y * 16, 16, 16));
			}
			//Draw the food
			if(_food != null)
				canvas.FillPie(new SolidBrush(Board.FoodColor), _food.X * 16, _food.Y * 16, 16, 16, 0, 360);
		}

		private void StartGame()
		{
			Level = STARTING_LEVEL;
			CurrentLives = STARTING_LIVES;
			StartLevel();
			_gameLoop.Interval = 1000 / 60;
			_gameLoop.Start();
			_snakeLoop.Start();
		}

		private void StartNextLevel()
		{
			Level++;
			StartLevel();
		}

		private void StartLevel()
		{
			GameOver = false;
			_snake.Clear();
			Score = 0;
			_direction = 1;
			_snake.Add(new DrawingItem(((pbCanvas.Width / 16) / 2), ((pbCanvas.Height / 16) / 2)));
			_snakeRate = 2.0f * Level;
			_snakeLoop.Interval = (int)(1000 / _snakeRate);
			Board = BoardFactory.GetBoard(Level, pbCanvas.Width, pbCanvas.Height);
			Text = string.Format("Snake: {0}", Board.Name);
			GenerateFood();
		}

		private void GenerateFood()
		{
			if (Score == MAX_LEVEL_SCORE)
			{
				_food = null;
				return;
			}
			var random = new Random(DateTime.Now.Millisecond);
			_food = new DrawingItem(random.Next(0, pbCanvas.Width / 16), random.Next(0, pbCanvas.Height / 16));
			if (Board.Barriers.Any(barrier => barrier.X == _food.X && barrier.Y == _food.Y))
				GenerateFood();
		}
		#endregion Private Methods
	}
}
